const path = require("path");
const { rename } = require("fs/promises");
const { existsSync, mkdirSync } = require("fs");
const formidable = require("formidable");
const multiparty = require("multiparty");

// 判断文件类型
const judgeFileExt = (type = [], file = {}) => {
  if (Array.isArray(file)) {
    return file.every((_) => {
      const { ext } = path.parse(_.path);
      return type.includes(ext);
    });
  } else {
    const { ext } = path.parse(file.path);
    return type.includes(ext);
  }
};

// 文件上传，成功后返回文件的相对路径
const storeFile = async (dirname, file, key) => {
  // 文件移动的目录，不存在时创建
  const targetDir = path.join(__dirname, dirname);
  const { ext } = path.parse(file.path);
  const fileName = `${new Date().getTime()}_${key + ext}`;
  const targetFile = path.join(targetDir, fileName);
  !existsSync(targetDir) && mkdirSync(targetDir);
  try {
    await rename(file.path, targetFile);
    return Promise.resolve(targetFile);
  } catch (error) {
    return Promise.reject(error);
  }
};

module.exports = {
  upload1: (req, res, next) => {
    const form = new multiparty.Form({
      encoding: "utf8", // 表单字段编码，默认 utf8
      maxFieldsSize: 2 * 1024 * 1024, // 限制所有存储表单字段域的字节数（除去 file 字段），默认 2MB，单位 byte
      maxFields: 1000, // error 事件触发前，被解析的字段数，默认值 1000
      maxFilesSize: 26 * 1024, // autoFiles 属性值为 true 是生效，默认值 Infinity，单位 byte
      uploadDir: path.resolve(__dirname, "../../temp"),
    });

    form.on("progress", (bytesReceived, bytesExpected) => {
      const rate = ((bytesReceived / bytesExpected) * 100).toFixed(2) + "%";
      console.log("Event: progress", rate);
    });

    form.on("file", (name, file) => {
      console.log("Event: file");
    });

    form.on("field", (name, value) => {
      console.log("Event: field");
    });

    form.on("close", () => {
      console.log("Event: close");
    });

    form.on("aborted", () => {
      console.log("Event: aborted");
    });

    form.on("error", (err) => {
      console.log("Event: error");
    });

    form.parse(req, (err, fields, files) => {
      if (err) {
        res.json({
          code: 500,
          msg: `${err.name}: ${err.message}`,
        });
      }
      console.log("Event: parse");
    });
  },

  upload: (req, res, next) => {
    const form = new formidable({
      encoding: "utf-8", // 表单域的编码，默认值 utf-8
      uploadDir: path.resolve(__dirname, "../../temp"), // 上传文件的存放位置，默认值 os.tmpDir()。一般为上传后的临时文件夹，上传后调用 fs.rename() 方法对文件进行移动及重命名。
      keepExtensions: true, // 上传后文件是否使用原扩展名，默认值 false
      maxFileSize: 2 * 1024 * 1024, // 上传文件的字节数，默认值 200 * 1024 * 1024
      maxFields: 1000, // 限制 Querystring 解析器将解码的字段数，默认值 1000
      maxFieldsSize: 20 * 1024 * 1024, // 限制所有存储表单字段域的字节数（除去 file 字段），默认值 20 * 1024 * 1024
      hash: false, // 是否对上传文件进行 hash 较验。默认值 false
      multiples: true, // 是否允许多个文件上传，默认值 false
    });

    /**
     * @method parse 解析请求中包含的数据
     * @param fields 文本域
     * @param files 上传的文件
     */
    form.parse(req, async (err, fields, files) => {
      console.log("Event: parse");
      if (err) {
        res.json({
          code: 500,
          msg: `${err.name}: ${err.message}`,
        });
      } else if (JSON.stringify(files) === "{}") {
        res.json({
          code: 4001,
          msg: "缺少参数~",
        });
      } else if (!judgeFileExt([".png", ".jpg"], files.file)) {
        res.json({
          code: 5002,
          msg: "此文件类型不允许上传~",
        });
      } else {
        try {
          let filePath = null;
          if (Array.isArray(files.file)) {
            filePath = await Promise.all(
              files.file.map((item, index) =>
                storeFile("../../data", item, index)
              )
            );
          } else {
            filePath = await storeFile("../../data", files.file, 0);
          }
          res.json({
            code: 200,
            data: {
              path: filePath,
            },
            msg: "文件上传成功~",
          });
        } catch (error) {
          console.log(error, "[----error");
          res.json({
            code: 5003,
            msg: "文件存储失败~",
          });
        }
      }
    });

    // 每接收到一个解析的数据块，便会触发。可以根据该事件更新进度条
    form.on("progress", (bytesReceived, bytesExpected) => {
      const rate = ((bytesReceived / bytesExpected) * 100).toFixed(2) + "%";
      console.log("Event: progress", rate);
    });

    // 每接收到一个字段键值对，便会触发
    form.on("field", (name, value) => {
      console.log("Event: field");
    });

    // 每有一个新文件开始上传，便会触发
    form.on("fileBegin", (name, file) => {
      console.log("Event: fileBegin");
    });

    // 每接收到一个文件字段值，便会触发
    form.on("file", (name, file) => {
      console.log("Event: file");
    });

    // 上传流中出现错误，便会触发。当出现错误时，若想要继续触发 request 的 data 事件，则必须手动调用 request.resume() 方法
    form.on("error", (err) => {
      console.log("Event: error");
    });

    // 用户中止请求，便会触发。socket 中的 timeout 和 close 事件也会触发该事件，当该事件触发之后，error事件也会触发
    form.on("aborted", () => {
      console.log("Event: aborted");
    });

    // 请求完全接收，且文件已上传到服务器中，便会触。此时可以发送请求到客户端
    form.on("end", () => {
      console.log("Event: end");
    });
  },
};
